#!/bin/sh

e2e_test_install() {
  PGCONFIG=postgresconf

  create_or_replace_cluster "$CLUSTER_NAME" "$CLUSTER_NAMESPACE" 1 \
    --set cluster.create=false \
    --set cluster.configurations.sgPostgresConfig="$PGCONFIG"

  deploy_curl_pod "$CLUSTER_NAMESPACE"

  wait_pods_running "$CLUSTER_NAMESPACE" 1

  build_default_config
}

build_default_config() {
  if kubectl get sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG"
  then
    echo "Saving Pgconfig $PGCONFIG"

    DEFAULT_PGCONFIG_CR="$(kubectl get sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG"  -o json \
      | jq 'del(.metadata.creationTimestamp) | del(.metadata.generation) | del(.metadata.resourceVersion) | del(.metadata.selfLink) | del(.metadata.uid)' )"

    echo_raw "$DEFAULT_PGCONFIG_CR" > "$LOG_PATH/default-pgconfig-cr.json"

    DEFAULT_PGCONFIG="$(cat "$LOG_PATH/default-pgconfig-cr.json" \
      | jq 'del(.apiVersion) | del(.kind)'  \
      | jq '.spec."postgresql.conf" = (.spec."postgresql.conf"|to_entries|map(.key + "='"'"'" + .value + "'"'"'")|join("\n"))')"

    echo_raw "$DEFAULT_PGCONFIG" > "$LOG_PATH/default-pgconfig.json"

    kubectl delete sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG" || true
  else
    echo "Pgconfig $PGCONFIG not installed"
    return 1
  fi

}

e2e_test_after_all() {
  unset PGCONFIG
}

e2e_test() {
  run_test "Check that a created pgconfig can be accessed directly through the API" check_pgconfig_directly

  run_test "Check that a created pgconfig is included in the response" check_pgconfig_in_list

  run_test "Check that a pgconfig was removed from the list of pgconfigs after its deletion" check_pgconfig_removed_from_list

  run_test "Check that a pgconfig was removed from direct access after its deletion" check_pgconfig_deletion_directly

  run_test "Postgres config creation through the API" test_pgconfig_create_with_api

  run_test "Check that a pgconfig created with the API is visible" api_created_pgconfig_visible

  run_test "Postgres config update with the api" test_update_pgconfig_with_api

  run_test "Check that pgconfig changes are reflected in the api" test_api_updated_pgconfig_is_visible

  run_test "Postgres config update with the api and mutated value" test_mutate_pgconfig_with_api

  run_test "Check that pgconfig changes are reflected in the api and mutated value" test_api_mutate_pgconfig_is_visible

  run_test "Check pgconfig deletion with api" test_delete_pgconfig_with_api

  run_test "Check that pgconfig deletions with the API are reflected in the API" test_api_delete_pgconfig_is_invible

  run_test "Constraint violations should be detected" test_constraint_violation_api_error

  run_test "User with wrong credentials should not be authenticated" test_authentication_api_error
}

create_pgconfig() {
  cat "$LOG_PATH/default-pgconfig-cr.json" | kubectl apply -f -

  wait_until eval 'kubectl get sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG"'
}

delete_pgconfig_only() {
  echo "Deleting pgconfig if there is any"
  if kubectl get sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG"
  then
    echo "Pgconfig $PGCONFIG found, deleting..."

    kubectl delete sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG"

    wait_until eval '! kubectl get sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG"'
  else
    echo "No pgconfig found"
  fi
}

check_pgconfig_in_list() {
  create_pgconfig

  if run_curl -r "stackgres/sgpgconfigs" -n "$CLUSTER_NAMESPACE" \
    | jq -r ".[] | select ( .metadata.namespace == \"$CLUSTER_NAMESPACE\") | select ( .metadata.name == \"$PGCONFIG\") | .metadata.name" \
    | grep -q "^$PGCONFIG$"
  then
    echo "Postgres config $PGCONFIG included in json response"
    return 0
  else
    echo "Postgres config $PGCONFIG not included in json response"
    return 1
  fi
}

get_pgconfig_status() {
  run_curl -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgpgconfigs/$PGCONFIG" -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}"
}

check_pgconfig_directly() {
  local HTTP_STATUS

  create_pgconfig

  HTTP_STATUS="$(get_pgconfig_status)"

  if [ "$HTTP_STATUS" -eq "200" ]
  then
    echo "Postgres config $PGCONFIG was found by the api"
    return 0
  else
    echo "Postgres config $PGCONFIG was not found by the api"
    return 1
  fi
}

check_pgconfig_removed_from_list() {
  delete_pgconfig_only

  if run_curl -r "stackgres/sgpgconfigs" -n "$CLUSTER_NAMESPACE" \
    | jq -r ".[] | select ( .metadata.namespace == \"$CLUSTER_NAMESPACE\") | select ( .metadata.name == \"$PGCONFIG\") | .metadata.name" \
    | grep -q "^$PGCONFIG$"
  then
    echo "Postgres config $PGCONFIG wasn't removed from cache"
    return 1
  else
    echo "Postgres config $PGCONFIG was removed from cache"
    return 0
  fi
}

check_pgconfig_deletion_directly() {
  local HTTP_STATUS

  delete_pgconfig_only

  HTTP_STATUS="$(run_curl -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgpgconfigs/$PGCONFIG" -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}")"

  if [ "$HTTP_STATUS" -eq "404" ]
  then
    echo "Postgres config $PGCONFIG was not found by the api"
    return 0
  else
    echo "Postgres config $PGCONFIG was found by the api"
    return 1
  fi
}

create_pgconfig_with_api() {
  echo "Creating pgconfig $PGCONFIG with the operator API"

  delete_pgconfig_only

  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -r "stackgres/sgpgconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgconfig.json" -e '-X POST -w %{http_code} -o /dev/null')"

  if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "202" ] || [ "$HTTP_STATUS" = "204" ]
  then
    echo "Request acknowledged by the operator"
  else
    local ERROR_RESPONSE
    ERROR_RESPONSE="$(run_curl -r "stackgres/sgpgconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgconfig.json" -e '-X POST')"

    echo "Invalid response status $HTTP_STATUS. response: $ERROR_RESPONSE"
    return 1
  fi
}

test_pgconfig_create_with_api() {
  create_pgconfig_with_api

  if wait_until eval 'kubectl get sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG"'
  then
    echo "Postgres config created with the operator API"
  else
    echo "Postgres config wasn't created with the API"
    return 1
  fi
}

api_created_pgconfig_visible() {
  create_pgconfig_with_api

  check_pgconfig_directly
}

update_pg_config_paramter_with_api() {
  local HTTP_STATUS

  UPDATED_PGCONFIG="$(cat "$LOG_PATH/default-pgconfig-cr.json" \
    | jq 'del(.apiVersion) | del(.kind)' \
    | jq ".spec[\"postgresql.conf\"][\"$1\"] = $2" \
    | jq '.spec."postgresql.conf" = (.spec."postgresql.conf"|to_entries|map(.key + "='"'"'" + .value + "'"'"'")|join("\n"))' )"

  echo_raw "$UPDATED_PGCONFIG" > "$LOG_PATH/updated-pgconfig.json"

  HTTP_STATUS="$(run_curl -r "stackgres/sgpgconfigs" -d "$LOG_PATH/updated-pgconfig.json" -e '-X PUT -w %{http_code} -o /dev/null')"

  if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "202" ] || [ "$HTTP_STATUS" = "204" ]
  then
    echo "Request acknowledged by the operator"
  else
    local ERROR_RESPONSE
    ERROR_RESPONSE="$(run_curl -r "stackgres/sgpgconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/updated-pgconfig.json" -e '-X PUT')"
    echo "Invalid response status $HTTP_STATUS. response: $ERROR_RESPONSE"
    return 1
  fi
}

get_pgconfig_from_api() {
  run_curl -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgpgconfigs/$PGCONFIG" -n "$CLUSTER_NAMESPACE"
}

test_update_pgconfig_with_api() {
  create_pgconfig

  update_pg_config_paramter_with_api 'shared_buffers' '"512MB"'

  if kubectl get sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG" -o "jsonpath={.spec['postgresql\.conf'].shared_buffers}" \
    | grep -q '^512MB$'
  then
    echo "Postgres config was updated"
  else
    echo "Postgres config was not updated"
    return 1
  fi
}

test_api_updated_pgconfig_is_visible() {
  update_pg_config_paramter_with_api 'shared_buffers' '"512MB"'

  if get_pgconfig_from_api | jq '.spec["postgresql.conf"]' -r \
    | grep -q "shared_buffers='512MB'"
  then
    echo "Postgres config updates are being reflected in the api"
  else
    echo "Postgres config updates aren't being reflected in the api"
    return 1
  fi
}

test_mutate_pgconfig_with_api() {
  create_pgconfig

  update_pg_config_paramter_with_api 'work_mem' '"32768kB"'

  if kubectl get sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG" -o "jsonpath={.spec['postgresql\.conf'].work_mem}" \
    | grep -q '^32MB$'
  then
    echo "Postgres config was updated with mutated value"
  else
    echo "Postgres config was not updated with mutated value"
    return 1
  fi
}

test_api_mutate_pgconfig_is_visible() {
  update_pg_config_paramter_with_api 'work_mem' '"32768kB"'

  if get_pgconfig_from_api | jq '.spec["postgresql.conf"]' -r \
    | grep -q "work_mem='32MB'"
  then
    echo "Postgres config updates are being reflected in the api with mutated value"
  else
    echo "Postgres config updates aren't being reflected in the api with mutated value"
    return 1
  fi
}

delete_pgconfig_with_api() {
  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -r "stackgres/sgpgconfigs" -n "$CLUSTER_NAMESPACE"  -d "$LOG_PATH/default-pgconfig.json" -e '-X DELETE -w %{http_code} -o /dev/null')"

  if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "202" ] || [ "$HTTP_STATUS" = "204" ]
  then
    echo "Request acknowledged by the operator"
  else
    local ERROR_RESPONSE
    ERROR_RESPONSE="$(run_curl -r "stackgres/sgpgconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgconfig.json" -e '-X PUT')"
    echo "Invalid response status $HTTP_STATUS. response: $ERROR_RESPONSE"
    return 1
  fi
}

test_delete_pgconfig_with_api() {
  create_pgconfig

  delete_pgconfig_with_api

  if wait_until eval '! kubectl get sgpgconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGCONFIG"'
  then
    echo "pgconfig was deleted"
  else
    echo "pgconfig was not deleted"
    return 1
  fi
}

test_api_delete_pgconfig_is_invible() {
  create_pgconfig

  delete_pgconfig_with_api

  if wait_until eval '[ "$(get_pgconfig_status)" = "404" ]'
  then
    echo "Postgres config removed from the API"
  else
    echo "Postgres config removed from the API"
    return 1
  fi
}

test_constraint_violation_api_error() {
  INVALID_PGCONFIG="$(cat "$LOG_PATH/default-pgconfig.json" | jq 'del (.spec.postgresVersion)' )"

  echo_raw "$INVALID_PGCONFIG" > "$LOG_PATH/invalid-pgconfig.json"

  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -r "stackgres/sgpgconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/invalid-pgconfig.json" -e '-X POST -w %{http_code} -o /dev/null')"

  assert_string_equal "422" "$HTTP_STATUS"
}

test_authentication_api_error() {
  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgpgconfigs"  -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}")"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgpgconfig/$PGCONFIG" -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}")"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgpgconfig" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgconfig.json" -e '-X POST -w %{http_code} -o /dev/null')"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgpgconfig" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgconfig.json" -e '-X PUT -w %{http_code} -o /dev/null')"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgpgconfig" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgconfig.json" -e '-X DELETE -w %{http_code} -o /dev/null')"

  check_authentication_error
}

check_authentication_error() {
  if [ "$HTTP_STATUS" = "401" ]
  then
    echo "Request returned expected authentication error"
    return 0
  else
    echo "Request returned unexpected response status $HTTP_STATUS instead of the expected authentication error."
    return 1
  fi
}